﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using DataProcessing;
using Gene.Render;
namespace Gene
{
    /// <summary>
    /// win_2DPlane.xaml 的交互逻辑
    /// </summary>
    public partial class win_2DPlane : Window
    {
        public const int ZOOM2D_RESOLUTION = 800;
        int INTERVAL_COUNT = 10;
        Label[] _labels ;
        int _chromeIndex;
        public win_2DPlane(int chromeIndex)
        {
            _chromeIndex = chromeIndex;
            InitializeComponent();
            Reset();
            _dragStart = NULL_POINT;
        }

        private void Reset()
        {
            if (canvasAnnotation != null)
            {
                canvasAnnotation.Children.Clear();
            }

            mainPanel.Children.Clear();
            AddXMarkerLabel();
            AddYMarkerLabel();
            Label lblChromeName = new Label();
            lblChromeName.Margin = new Thickness(10, 2, 0, 0);
            lblChromeName.HorizontalAlignment = HorizontalAlignment.Left;
            lblChromeName.VerticalAlignment = VerticalAlignment.Top;
            

            lblChromeName.Content = Settings.Instance.ChromeSettingList[_chromeIndex].DisplayName;

            mainPanel.Children.Add(lblChromeName);
           

        }

        private void AddXMarkerLabel()
        {
            INTERVAL_COUNT = XZPlane.MARKER_COUNT;
            _labels = new Label[INTERVAL_COUNT + 1];

            for (int i = 0; i <= INTERVAL_COUNT; i++)
            {
                _labels[i] = new Label();
                _labels[i].HorizontalAlignment = HorizontalAlignment.Left;
                _labels[i].VerticalAlignment = VerticalAlignment.Top;
                _labels[i].Name = "Label" + (i + 1).ToString();
                _labels[i].Height = 28;
                _labels[i].Margin = new Thickness(15 + i * 80, BASELINE_Y + 10, 0, 0);
                mainPanel.Children.Add(_labels[i]);
            }
        }

        const int Y_MARKER_COUNT = 6;
        const int MARKER_LABEL_HEIGHT = 28;
        Label[] _yLabels;
        private void AddYMarkerLabel()
        {

            _yLabels = new Label[Y_MARKER_COUNT + 1];

            for (int i = 0; i <= Y_MARKER_COUNT; i++)
            {
                _yLabels[i] = new Label();
                _yLabels[i].HorizontalAlignment = HorizontalAlignment.Left;
                _yLabels[i].VerticalAlignment = VerticalAlignment.Top;
                _yLabels[i].Name = "Y_" + (i + 1).ToString();
                _yLabels[i].Height = MARKER_LABEL_HEIGHT;

                mainPanel.Children.Add(_yLabels[i]);
            }
        }
        protected void DrawLine(Point startPt, Point endPt, double thickness, Brush brush,Canvas target = null)
        {

            Path myPath =  GraphicUtil.DrawLine(startPt, endPt, thickness, brush);
            if (target == null)
                mainPanel.Children.Add(myPath);
            else
                target.Children.Add(myPath);
        }

        const int BASELINE_Y = 400;
        const int MAX_ACCUMLATION_LEN = 350;
        const int DATA_DRAWING_AREA_START = 50;
        ValueRange _range;
        public void Draw(ValueRange range)
        {

            range = ChromePairData.Data[_chromeIndex].GetActualWindow(range);
            
            InnerDraw(range);
            
        }

        /// <summary>
        /// 内部画图不需要坐标转换
        /// </summary>
        /// <param name="range"></param>
        private void InnerDraw(ValueRange range)
        {
            Reset();
            _range = range;
            CalculationCache[] zoom2DCache = null;

            zoom2DCache = FastCalculation.ComputeCache((uint)_chromeIndex,
                                        range,
                                        (int)ZOOM2D_RESOLUTION,
                                        false);
            uint[] accumulations = CalculationCache.getAccumulations(zoom2DCache);




            DrawXCoordinate(range);
            DrawAccumulations(accumulations);
            DrawYCoordinate((int)accumulations.Max());
            if (!AnnotationData.Ready)
            {
                AnnotationGrid.Visibility = System.Windows.Visibility.Hidden;
                return;
            }

            HandleAnnotation(range);
        }

        private int DrawAccumulations(uint[] accumulations)
        {
            uint accMax = 0;
            int i;
            
            accMax = accumulations.Max();

            for (i = 0; i < accumulations.Length; i++)
            {
                Point startP = new Point(DATA_DRAWING_AREA_START + i, BASELINE_Y);
                Point endP = new Point(DATA_DRAWING_AREA_START + i, BASELINE_Y - (double)accumulations[i] / accMax * MAX_ACCUMLATION_LEN);
                DrawLine(startP, endP, 1, Brushes.Green);
            }
            return i;
        }
        private void DrawYCoordinate(int maxAccumulation)
        {
            int[] yPosOfLabel =Util.GenerateMarker(BASELINE_Y-MAX_ACCUMLATION_LEN, BASELINE_Y,Y_MARKER_COUNT);
            int[] contentOfLabel = Util.GenerateMarker(0,maxAccumulation,Y_MARKER_COUNT);

            int len = contentOfLabel.Length;
            for(int i=0;i<len;i++)
            {
                _yLabels[i].Margin = new Thickness(1, yPosOfLabel[i]-_yLabels[i].Height/2, 0, 0);
                _yLabels[i].Content = contentOfLabel[len - i-1].ToString();
                _yLabels[i].Foreground = new SolidColorBrush(Colors.Green);
            }


            
        }
        private void DrawXCoordinate(ValueRange range)
        {
            int i = 0;
            DrawLine(new Point(15, BASELINE_Y), new Point(885, BASELINE_Y), 4, Brushes.Red);

            for (i = 0; i <= INTERVAL_COUNT; i++)
            {
                DrawLine(new Point(DATA_DRAWING_AREA_START + i * 80, BASELINE_Y), new Point(DATA_DRAWING_AREA_START + i * 80, BASELINE_Y + 4), 3, Brushes.Red);
            }
            uint delta = (range.End - range.Start) / (uint)(INTERVAL_COUNT);
            if (range.End - range.Start - delta * INTERVAL_COUNT > 5)
                delta++;
            for (i = 0; i < INTERVAL_COUNT; i++)
            {
                _labels[i].Content = range.Start + delta * i;
            }
            _labels[INTERVAL_COUNT].Content = range.End;
            
        }

        private void HandleAnnotation(ValueRange range)
        {
            AnnotationData lastSelected = _lastSelected;
            LoadAnnotation(Settings.Instance.ChromeSettingList[_chromeIndex].Name, range);
            DrawAllAnnotations();
            this.comboBoxAnnotationList.ItemsSource = _annotationData;


            if (_lastSelected != null && _annotationData.Contains(_lastSelected))
            {

                this.comboBoxAnnotationList.SelectedIndex =_annotationData.IndexOf(_lastSelected);
                GotoSelectedAnnotation(_lastSelected);
            }
            else
            {
                this.comboBoxAnnotationList.SelectedIndex = 0;
            }
        }
    
        List<AnnotationData> _annotationData = null;
        
        private void LoadAnnotation(String chromeName,ValueRange window)
        {
            if (!AnnotationData.Ready)
                return;
            _annotationData = AnnotationData.Search(window, chromeName);
        }


        AnnotationData _lastSelected;
        /// <summary>
        /// todo:好像有问题，放大缩小时候，不画annotation，
        /// 选一下就画了。
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void comboBoxAnnotationList_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            _lastSelected = this.comboBoxAnnotationList.SelectedValue as AnnotationData;
            if (_lastSelected == null)
                return;


            GotoSelectedAnnotation(_lastSelected);
            

        }

        private void GotoSelectedAnnotation(AnnotationData selected)
        {
            int index = _annotationData.IndexOf(selected);

            this.AnnotationScroll.ScrollToVerticalOffset(index * HEIGH_OF_ONE_ANNOTATION);
            labelGeneName.Content = _lastSelected.GeneName;
            labelDirection.Content = _lastSelected.Dir.ToString();
        }
        void DrawAllAnnotations()
        {
            canvasAnnotation.Children.Clear();
            canvasAnnotation.Height = HEIGH_OF_ONE_ANNOTATION * _annotationData.Count;
            for(int i=0;i<_annotationData.Count;i++)
            {
                DrawAnnotation(_annotationData[i],i);
            }
            canvasAnnotation.InvalidateVisual();

        }

        const int ANNOTATION_BASELINE_Y = 100;//
        const int ANNOTATION_THICKNESS = 20;//annotation range的线的粗细。
        const int XSON_LEN = 60;//xson的高度
        const int ANNOTATION_TEXT_HEIGHT =30;//上面文字说明的高度。
        const int ANNOTATION_PADDING =10;//各annotation之间的间隔
        const int HEIGH_OF_ONE_ANNOTATION = ANNOTATION_BASELINE_Y + ANNOTATION_THICKNESS + ANNOTATION_PADDING;
        private void DrawAnnotation(AnnotationData annotation,int index)
        {
            

            //0. 画annotation 文字
            //DrawDirection(annotation.Dir);
            DrawAnnotationText(annotation, index);
            //1.画range
            Point rangeStartPoint = DrawAnnotationRange(annotation,index);

            //2.画xson
            DrawXson(annotation, rangeStartPoint.Y);

          
          
            
            //end point
        }

        private Point DrawAnnotationRange(AnnotationData annotation,int index)
        {
            Point startPoint;
            Path path;
            int start = ValueToScreenX((int)annotation.Range.Start);
            int end = ValueToScreenX((int)annotation.Range.End);
            int Y = ANNOTATION_BASELINE_Y + HEIGH_OF_ONE_ANNOTATION * index;
            startPoint = new Point(start, Y);
            Point endPoint = new Point(end, Y);
            path = GraphicUtil.DrawLine(startPoint, endPoint, ANNOTATION_THICKNESS, Brushes.Blue);
            canvasAnnotation.Children.Add(path);
            return startPoint;

        }

        private void DrawXson(AnnotationData annotation, double yBase)
        {
            //a.是否值得画？
            if (IsWorthDraw(annotation.XSonInfo, annotation.Range))
            {
                double yStart = yBase - ANNOTATION_THICKNESS/2;
                foreach (ValueRange range in annotation.XSonInfo)
                {
                    ValueRange xsonRange = new ValueRange();
                    xsonRange.Start = (uint)ValueToScreenX((int)range.Start);
                    xsonRange.End = (uint)ValueToScreenX((int)range.End);
                    
                    //要右移过去，因为刷子是以本点为中点的
                    Point xsonStartPoint = new Point(xsonRange.Start + xsonRange.Size / 2,yStart);
                    Point ssonEndPoint = new Point(xsonStartPoint.X, yStart- XSON_LEN);
                    DrawLine(xsonStartPoint, ssonEndPoint, xsonRange.Size, Brushes.Black, canvasAnnotation);

                }
            }

           
        }

        private int ValueToScreenX(int value)
        {
            return (int)Util.ToSameScale(_range.Start, _range.End, DATA_DRAWING_AREA_START, DATA_DRAWING_AREA_START + ZOOM2D_RESOLUTION, value);
        }

        private int ScreenXToValue(int x)
        {
            return (int)Util.ToSameScale(DATA_DRAWING_AREA_START, DATA_DRAWING_AREA_START + ZOOM2D_RESOLUTION, _range.Start, _range.End,x);
        }

        /// <summary>
        /// 确保最小的范围也能占至少一个像素
        /// </summary>
        /// <param name="xonInfo"></param>
        /// <param name="xSonWholeRange"></param>
        /// <returns></returns>
        private bool IsWorthDraw(List<ValueRange> xonInfoList,ValueRange xSonWholeRange)
        {
            bool ret = true;
            double pointIn1Pixel =  ((double)_range.Size)/ZOOM2D_RESOLUTION;

            var filteredResult = from xonInfo in xonInfoList
                                 where xonInfo.Size >= pointIn1Pixel
                                 select xonInfo;

            if (filteredResult.Count() != xonInfoList.Count)
            {
                return false;
            }

          
            return ret;
        }
        const int ARROW_Y = 10;
        const int ARROW_START = 5;
        const int ARROW_END = 100;
        const int ARROW_THICKNESS = 5;
        void DrawAnnotationText(AnnotationData data,int index)
        {
            const int GENE_NAME_X =10;
            const int TEXT_HEIGHT =28;
            const int TEXT_PADDING = ANNOTATION_TEXT_HEIGHT-TEXT_HEIGHT;
            
            Label geneNameLbl = new Label{Content=data.ToString(),Height=TEXT_HEIGHT};
            
            int txtStartY = index *HEIGH_OF_ONE_ANNOTATION + TEXT_PADDING;
            geneNameLbl.Margin=new Thickness(GENE_NAME_X,txtStartY,0,0);

            canvasAnnotation.Children.Add(geneNameLbl);
            Label directionLbl = new Label{Content="Orientation: "+data.Dir.ToString()};

            double GENE_DIRECTION_X = GENE_NAME_X +250;
            directionLbl.Margin = new Thickness(GENE_DIRECTION_X, txtStartY, 0, 0);
            canvasAnnotation.Children.Add(directionLbl);
           // labelDirection.Content = dir.ToString();
        }
        const double ZOOM_STEP = 1.5;
        private void mainPanel_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            
            if (e.Delta >= 0)
            {
                ZoomIn(ZOOM_STEP);
            }
            else
            {
                ZoomOut(ZOOM_STEP);
            }
        }

        /// <summary>
        /// 放大
        /// </summary>
        /// <param name="ratio"></param>
        private void ZoomIn(double ratio)
        {
            
            ValueRange newRange =  _range.Scale(1/ratio,ScreenXToValue( (int)_mousePos.X));
            newRange.Truncate(ChromePairData.AllWindowMax);
            InnerDraw(newRange);
        }

        private void ZoomOut(double ratio)
        {
            ValueRange newRange = _range.Scale(ratio,ScreenXToValue((int)_mousePos.X));
            newRange.Truncate(ChromePairData.AllWindowMax);
            InnerDraw(newRange);
        }

        Point _mousePos;
        private void AnnotationGrid_MouseMove(object sender, MouseEventArgs e)
        {
            _mousePos = e.GetPosition(mainPanel);

        }

        Point _dragStart;
        Point NULL_POINT = new Point(int.MaxValue, int.MaxValue);
        private void mainPanel_MouseMove(object sender, MouseEventArgs e)
        {
            _mousePos = e.GetPosition(mainPanel);


            //check dragging.
            if (e.LeftButton != MouseButtonState.Pressed)
            {
                return;
            }
            if(_dragStart.Equals(NULL_POINT))
            {
                return;
                
            }

            int offset = (int)(_mousePos.X - _dragStart.X);

            long valueOffset = -offset* PointPerPixel(); //ScreenOffsetToValueOffset(-offset);

            ValueRange range = _range.Add((int)valueOffset);
            InnerDraw(range);
            _dragStart = _mousePos;

        }

        int PointPerPixel()
        {
            return (int)(_range.Size / ZOOM2D_RESOLUTION);
        }
        long ScreenOffsetToValueOffset(int screenOffset)
        {
            return _range.Size / ZOOM2D_RESOLUTION * screenOffset;
        }

        private void mainPanel_DragOver(object sender, DragEventArgs e)
        {
            //e.
        }

        private void mainPanel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 1)
            {
                _dragStart = e.GetPosition(mainPanel);

            }


            if (e.ClickCount == 2)
                ZoomIn(ZOOM_STEP);
        }

        private void mainPanel_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ClickCount == 2)
                ZoomOut(ZOOM_STEP);
        }

        private void mainPanel_MouseUp(object sender, MouseButtonEventArgs e)
        {
            _dragStart = NULL_POINT;
        }

       
    
    }
}
